package com.yunxi.service.localService.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import com.yunxi.dao.code.CodeCreateMapper;
import com.yunxi.model.code.CfgGetKey;
import com.yunxi.service.localService.CodeCreateService;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 序列号规则生成工具类
 */
@Service(value = "codeCreateServiceImpl")
public class CodeCreateServiceImpl extends ServiceImpl<CodeCreateMapper, CfgGetKey> implements CodeCreateService {

    private static final Logger logger = LoggerFactory.getLogger(CodeCreateServiceImpl.class);

    @Autowired
    RedisTemplate redisTemplate;

    public static String DATE_FORMAT_ONE = "yyyyMMddHHmmss";
    public static String DATE_FORMAT_TWO = "yyyyMMdd";
    public static String DATE_FORMAT_THREE = "yyMM";
    public static String DATE_FORMAT_FOUR = "yyMMdd";
    /**
     * 前缀与表的对应关系
     */
    private static ConcurrentMap<String, String> prefixToTable = new ConcurrentHashMap<>();

    /**
     * 按初始化前缀与表对应关系
     */
    static {
        prefixToTable.put("PS", "pms_order_oem");
        prefixToTable.put("OC", "pms_order_vehicle");
        prefixToTable.put("WP", "pms_otd_week_plan");
        prefixToTable.put("WUPL", "pms_otd_week_upload");
        prefixToTable.put("WPD", "pms_otd_week_plan_detail");
        prefixToTable.put("WPLAN", "pms_otd_if_produce_plan");
        prefixToTable.put("MPLAN", "pms_otd_if_month_plan");
        prefixToTable.put("SOP", "pms_sto_stock_out_plan");
        prefixToTable.put("SO", "pms_sto_stock_out");
        prefixToTable.put("SIP", "pms_sto_stock_in_plan");
        prefixToTable.put("SI", "pms_sto_stock_in");
        prefixToTable.put("FR", "pms_sto_frozen_reason");
        prefixToTable.put("PST", "pms_sto_manage");
        prefixToTable.put("TPP", "pms_sto_transport_plan");
        prefixToTable.put("PMS", "pms_partner");
        prefixToTable.put("MP", "pms_otd_month_plan");
        prefixToTable.put("PCHG", "pms_otd_plan_change_apply");
        prefixToTable.put("SLO", "pms_sto_vehicle_lend_order");
        prefixToTable.put("FST", "pms_sto_manage");
        prefixToTable.put("SRO", "pms_sto_repair_order");
        prefixToTable.put("SGC", "pms_sto_storage_company");
        prefixToTable.put("SGS", "pms_sto_storage_staff");
        prefixToTable.put("SCS", "pms_sto_change_storage");
        prefixToTable.put("PC", "pms_otd_project_car_order");
        prefixToTable.put("OCL", "pms_order_cancel_reason");
        prefixToTable.put("OED", "pms_order_modify_apply");
        prefixToTable.put("ORJ", "pms_order_vehicle_reject");
        prefixToTable.put("BB", "pms_order_customer");
        prefixToTable.put("P", "pms_otd_order_so");
        prefixToTable.put("TDR", "pms_sat_test_drive_route");
        prefixToTable.put("CC", "pms_sat_customer_clue");
        prefixToTable.put("VRA", "pms_vehicle_retur_apply");
        prefixToTable.put("PDI", "pms_vehicle_pdi_bill");
        prefixToTable.put("MA", "pms_market_activity");
        prefixToTable.put("LS", "pms_sat_customer_clue");
        prefixToTable.put("RC", "pms_dynamic_rebate_commission");
        prefixToTable.put("D", "pms_vip");

    }

    @Override
    public String createCode(String prefix, String pattern, Integer serialNumberLength) throws RuntimeException {

        /**
         * 通过数据库的实现方式不能支持高并发且也不能支持分布式
         * 通过redis来实现从缓存中获取最新序列号
         * 并将最新序列号更新至数据库中
         * 涉及点1,系统启动后需从数据库中获取最新的序号值 (分布式系统,不能从数据库中初始化,如果redis中没有了,就从1开始继续)
         * 2,将最新的序列号更新到数据库中,当序列号值比数据库中的序列号值大时,才更新记录;(不需处理此步)
         * 3,增加计划任务,定期将数据设置为1 ;  如订单保存,只支持4位数,这个时候就会越界了;(不需要做计划任务,程序中判断,如果大于max值了,重新开始)
         */
        DateFormat sdf = new SimpleDateFormat(pattern);
        String infix = sdf.format(new Date());

        String tableName = prefixToTable.get(prefix);

        //从redis中获取序列号值,并将redis中的值加1;key为对应的业务表名
        RedisAtomicLong redisAtomicLong = new RedisAtomicLong("sequence : " + tableName,
            redisTemplate.getConnectionFactory());
        Long currentSeq = redisAtomicLong.incrementAndGet();//获取流水号  如果redis没有值,则默认为1,并在redis中会自动生成一条值为1的数据
        //设置超时时间,可以通过计划任务控制,可不用在此设置
//        if(currentSeq == null ){
//            redisAtomicLong.expire(1, TimeUnit.DAYS);
//        }

        //更新数据库  此时更新保存最新的数据,只保存最大值;  (不需要保存至数据库)
//        CfgGetKey cfgGetKey = new CfgGetKey ();
//        cfgGetKey.setCurrentNum(currentSeq == null ? 1 : currentSeq.intValue());
//        QueryWrapper upWrapper = new QueryWrapper();
//        upWrapper.eq("table_name",tableName);
//        int c = this.baseMapper.update(cfgGetKey,upWrapper);

        //后缀
        //超过max值的数据需要截取
        String suffix;
        if (currentSeq != null && currentSeq.toString().length() > serialNumberLength) {
            String currentSeqStr = currentSeq.toString();
            suffix = currentSeqStr.substring(currentSeqStr.length() - serialNumberLength);
            //如果截取后,后几位为0,则继续取值
            if (Integer.parseInt(suffix) == 0) {
                logger.info("序号超过最大值,且截取后为0 : ==== " + currentSeq);
                currentSeq = redisAtomicLong.incrementAndGet();
                currentSeqStr = currentSeq.toString();
                suffix = currentSeqStr.substring(currentSeqStr.length() - serialNumberLength);
            }

        } else {
            //如果小于,则自动补0
            suffix = String.format("%0" + serialNumberLength + "d", currentSeq);
        }
//        System.out.println("测试超过max值的情况 : " + String.format("%0" + 4 + "d", 1238991));

        //完整的code由前缀+中缀+后缀组成
        String code = prefix + infix + suffix;
        return code;
    }

    @Override
    public String createCode(String prefix, Integer serialNumberLength) {
        return createCode(prefix, "yyyyMMddHHmmss", serialNumberLength);
    }

    public static void main(String args[]) {
        DateFormat sdf = new SimpleDateFormat("yyMM");
        String infix = sdf.format(new Date());
        System.out.println(infix);
    }
}
